﻿/*

   GWCA.NET 0.50.230.0  (BETA - NamedPipes branch)
	  by fritz [zechariah.ben.david@gmail.com]
 * 
   Copyright (C) hAx Studios Ltd. 2010
   http://gwcadotnet.googlecode.com
 
 * This class library is designed to duplicate and provide the
 * same functionality as GWCAonstants.au3 except for the .NET framework.
 
 * TODO:
 *      Ensure x64 Support
 *      Improve Exception handling
 *      Improve MemoryAccess class
 *      Update and write more documentation.

*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.IO.Pipes;

namespace Gwca.DotNet
{

    public class Win32Api
    {
        #region DLL Exports
        [DllImport("user32.dll")]
        public static extern IntPtr PostMessage(IntPtr hWnd, int imsg, int iwParam, int ilParam);
        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, int imsg, int iwParam, int ilParam);
        [DllImport("user32.dll")]
        public static extern bool GetMessage(out Message lpmsg, IntPtr hWnd, uint wmsgFilterMin, uint wmsgFilterMax);
        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(uint dwDesiredAccess, Boolean bInheritHandle, int dwProcessId);
        [DllImport("kernel32.dll")]
        public static extern Boolean CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string lpModuleName);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
        [DllImport("kernel32.dll")]
        public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        [DllImport("kernel32.dll")]
        public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out]Byte[] lpBuffer, uint nSize, uint lpNumberOfBytesRead);
        [DllImport("kernel32.dll")]
        public static extern Boolean WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, Byte[] lpBuffer, uint nSize, uint lpNumberOfBytesWritten);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GlobalAlloc(int uFlags, IntPtr dwBytes);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GlobalLock(IntPtr hMem);
        [DllImport("kernel32.dll")]
        public static extern bool GlobalUnlock(IntPtr hMem);
        #endregion
    }

    #region GWCA Enums

    public enum Ca : int
    {
        DisconnectPipe = 0x001,
        AliveRequest, IsAlive,
        Error = 0x050,
        CommandsBegin = 0x101,
        Attack, Dialog, CancelAction, ClearPacketQueue, QuestAbandon, DonateFaction, SetLogAndHwnd,
        Move, GoNpc, GoPlayer, GoSignpost, GoAgent,
        UseSkill, SetEventSkillMode, SetSkillbarSkill, SetAttribute, ChangeSecondProfession,
        ChangeWeaponSet, EquipItem, EquipItemById, UseItem, UseItemById,
        DropGold, DepositGold, WithdrawGold,
        ChangeTarget, TargetNearestFoe, TargetNearestAlly, TargetNextPartyMember, TargetNextFoe,
        TargetNearestItem, TargetCalledTarget,
        UseHero1Skill, UseHero2Skill, UseHero3Skill,
        CommandHero1, CommandHero2, CommandHero3, CommandAll, SetHeroMode,
        AddHero, KickHero, AddNpc, KickNpc,
        ChangeDistrict, ZoneMap, SwitchMode, InitMapLoad, SkipCinematic,
        DismissBuff, SendChat, SetTeamSize, FreeMem,
        Resign, ReturnToOutpost, EnterChallenge, TravelGH, LeaveGH,
        SetBag, PrepareMoveItem, MoveItem,
        IdentifyItem, IdentifyItemById, SalvageItem,
        SellItem, SellItemById, BuyIdKit, BuySuperiorIdKit,
        BuyItem, TraderRequest, TraderRequestSell, TraderRequestSellById,
        OpenChest, AcceptAllItems, PickupItem, DropItem, DropItemById,
        CommandsEnd,
        RequestsBegin = 0x301,
        GetCurrentTarget, GetMyId, GetMyMaxHP, GetMyMaxEnergy, GetMyNearestAgent, GetMyDistanceToAgent,
        Casting, SkillRecharge, SkillAdrenaline, GetSkillbarSkillId,
        GetTarget, GetAgentAndTargetPtr,
        GetBuildNumber, ChangeMaxZoom, GetLastDialogId, SetEngineHook,
        GetGold, GetBagSize, GetItemId,
        GetIdKit, GetSalvageKit,
        GetItemInfo, GetItemLastModifier, GetItemLastModifierById,
        FindItemByModelId, FindEmptySlot, FindGoldItem,
        GetItemPositionByItemId, GetItemPositionByModelId, GetItemPositionByRarity,
        GetItemModelIdById, GetItemInfoById,
        GetItemIdByAgent, GetItemInfoByAgent, GetItemLastModifierByAgent, GetNearestItemByModelId,
        GetMapLoading, GetMapId, MapIsLoaded, GetRegionAndLanguage, GetPing, GetLoggedIn, GetDead,
        GetBalthFaction, GetKurzFaction, GetLuxonFaction,
        GetTitleTreasure, GetTitleLucky, GetTitleUnlucky, GetTitleWisdom, GetTitleGamer, GetExperience,
        GetTitleSunspear, GetTitleLightbringer, GetTitleVanguard, GetTitleNorn, GetTitleAsura, GetTitleDeldrimor,
        GetTitleNorthMastery, GetTitleDrunkard, GetTitleSweet, GetTitleParty, GetTitleCommander, GetTitleLuxon, GetTitleKurzick,
        GetMapOverlayCoords, GetMapOverlayInfo, GetNearestMapOverlayToCoords, GetPartyInfo,
        GetAgentExist, GetProfessions, GetPlayerNumber, GetName, GetHP, GetRotation,
        GetSkill, GetCoords, GetWeaponSpeeds, GetSpiritRange, GetTeamId, GetCombatMode,
        GetModelMode, GetHpPips, GetEffects, GetHex, GetModelAnimation, GetEnergy, GetAgentPtr,
        GetType, GetLevel, GetNameProperties, GetMaxId, GetSpeed, GetAllegiance, GetWeaponType,
        GetModelState, GetIsAttacking, GetIsKnockedDown, GetIsMoving, GetIsDead, GetIsCasting, GetIsAttackedMelee,
        PlayerHasBuff, Hero1HasBuff, Hero2HasBuff, Hero3HasBuff,
        GetFirstAgentByPlayerNumber, GetNearestAgentToAgent, GetDistanceFromAgentToAgent,
        GetNearestAgentToAgentEx, GetNearestEnemyToAgentEx, GetNearestItemToAgentEx,
        GetNearestAgentByPlayerNumber, GetNearestEnemyToAgentByAllegiance, GetNearestAliveEnemyToAgent,
        GetNearestSignpostToAgent, GetNearestNpcToAgentByAllegiance, GetNearestAgentToCoords,
        GetNearestNpcToCoords, GetLoginNumber, GetNumberOfAgentsByPlayerNumber, GetNumberOfAliveEnemyAgents, GetNextItem,
        QuestCheck, QuestCoords, QuestActive, AllocMem, TraderCheck, TraderBuy, TraderSell,
        GetItemExtraId, GetItemExtraIdById, GetConnection,
        RequestsEnd
    };

    public enum ItemRarity : int
    {
        WHITE = 0x3D, BLUE = 0x3F, PURPLE = 0x42, GOLD = 0x40, GREEN = 0x43
    };

    public enum Bag : int
    {
        BackPack = 1, BeltPouch, Bag1, Bag2, EquipmentPack, UnclaimedItems = 7, Storage1, Storage2,
        Storage3, Storage4, Storage5, Storage6, Storage7, Storage8, StorageAnniversary
    };

    public enum Hero : int
    {
        Norgu = 1, Goren, Tahklora, MasterOfWhispers, AcolyteJin, Koss, Dunkoro, AcolyteSousuke,
        Melonni, ZhedShadowhoof, GeneralMorgahn, MargridTheSly, Olias = 14, Razah, MOX, Jora = 18,
        PyreFierceshot, Livia = 21, Kahmu, Gwen, Xandra, Vekk, Ogden
    };

    public enum HeroMode : int
    {
        Fight, Guard, Avoid
    }

    public enum Chat : int
    {
        All = 0, Guild, Team, Trade, Alliance, Emote = 14
    };

    public enum Misc : int
    {
        FLAG_RESET = 0x7F800000
    }
    #endregion

    public class Gw
    {
        #region GWCA Variables

        public static int[] CbVar = new int[2];
        public static Type CbType = typeof(int);

        // public static float[] CbVar = new float[2];

        #endregion

        #region GWCA.NET Variables

        #endregion

        #region GWCA Functions per GWCAConstonants.au3

        //** Converts 32-bit int from callbacks to float
        public static float IntToFloat(int intPtrValue)
        {
            //** TODO: Rewrite function w/out having to use unsafe context.
            unsafe
            {
                int x = intPtrValue;
                void* px = &x;
                float* pd = (float*)px;

                return *pd;
            }
        }

        //** Converts float from callbacks to 32-bint int
        public static int FloatToInt(float floatPtrValue)
        {
            //** TODO: Rewrite function w/out having to use unsafe context.
            unsafe
            {
                float x = floatPtrValue;
                void* px = &x;
                int* pd = (int*)px;

                return *pd;
            }
        }

        //** Posts message to Gw
        public static void Cmd(Ca msg)
        {
            Cmd(msg, 0, 0);
        }

        public static void Cmd(Ca msg, int wParam)
        {
            Cmd(msg, wParam, 0);
        }

        public static void Cmd(Ca msg, int wParam, int lParam)
        {
            CbVar[0] = 0;
            CbVar[1] = 0;

            GwcaPipe.SendMessage(GwcaPipe.MessageType.Is_Command, msg, wParam, lParam);
        }


        //** Sends message to Gw with callback id to return results
        public static int[] CmdCb(Ca msg)
        {
            return CmdCb(msg, 0);
        }

        public static int[] CmdCb(Ca msg, int wParam)
        {
            return CmdCb(msg, wParam, 0);
        }

        public static int[] CmdCb(Ca msg, int wParam, int lParam)
        {

            CbVar[0] = 0;
            CbVar[1] = 0;

            return GwcaPipe.SendMessage(GwcaPipe.MessageType.Is_Request, msg, wParam, lParam);
        }

        //** Move to x and y, overloaded to increase random amount of movement.
        public static void MoveEx(float x, float y)
        {
            MoveEx(x, y, 50);
        }

        public static void MoveEx(float x, float y, int random)
        {
            System.Random Rand = new Random();

            Cmd(Ca.Move, FloatToInt(x + Convert.ToSingle(Rand.Next(-random, random))), FloatToInt(y + Convert.ToSingle(Rand.Next(-random, random))));
        }

        public static int GetNearestAgentToCoords(float x, float y)
        {
            return CmdCb(Ca.GetNearestAgentToCoords, FloatToInt(x), FloatToInt(y))[1];
        }

        public static int[] GetNearestNpcToCoords(float x, float y)
        {
            return CmdCb(Ca.GetNearestNpcToCoords, FloatToInt(x), FloatToInt(y));
        }

        public static int[] GetNearestMapOverlayToCoords(float x, float y)
        {
            return CmdCb(Ca.GetMapOverlayCoords, FloatToInt(x), FloatToInt(y));
        }

        public static void TravelTo(int mapId)
        {
            TravelTo(mapId, 0);
        }

        public static void TravelTo(int mapId, int district)
        {
            GwcaTimer MapTimer = new GwcaTimer();
            int MapLoading, MapIsLoaded;


            Cmd(Ca.InitMapLoad);
            Cmd(Ca.ZoneMap, mapId, district);

            do
            {
                Gw.Sleep(195, 205);
                MapLoading = CmdCb(Ca.GetMapLoading)[0];

                if (MapTimer.Duration > 15000 && MapLoading != 2)
                {
                    Cmd(Ca.ZoneMap, 0, mapId);
                }
                MapIsLoaded = CmdCb(Ca.MapIsLoaded)[0];

            } while (MapLoading != 0 && MapIsLoaded != 1);

            MapTimer.Kill();
        }

        public static void MoveItem(int itemBag, int itemSlot, int newBag, int newSlot)
        {
            Cmd(Ca.PrepareMoveItem, itemBag, itemSlot);
            Gw.Sleep(20);
            Cmd(Ca.MoveItem, newBag, newSlot);
        }

        public static void MoveItemById(int itemId, int newBag, int newSlot)
        {
            Cmd(Ca.PrepareMoveItem, itemId, 0);
            Gw.Sleep(20);
            Cmd(Ca.MoveItem, newBag, newSlot);
        }

        //** Pickup items
        public static void PickupItems()
        {
            //** Picks up 12 items by default with 1012 as max distance
            PickupItems(12, 1012);
        }

        public static void PickupItems(int itemsToPickup, int maxDistance)
        {
            //** COMPLETE: Need to write function overloads to avoid putting these default values..

            int ItemsPickedUp = 0;
            int[] ClosestItem;          //** Will hold item id and distance of closest on screen item.

            do
            {
                //** Get information about the nearest item, the item id and distance from character.
                ClosestItem = CmdCb(Ca.GetNearestItemToAgentEx, -2);

                //** If there is no item at all, or if that item is further than MaxDistance, end.
                if (ClosestItem[0] == 0 || IntToFloat(ClosestItem[1]) > maxDistance)
                {
                    break;
                }
                else
                {
                    //** Send command to pickup item.
                    Cmd(Ca.PickupItem, ClosestItem[0]);

                    do
                    {
                        //** Wait 475 to 525ms to check if item has been picked up yet or not.
                        Gw.Sleep(475, 525);

                    } while (CmdCb(Ca.GetAgentExist, ClosestItem[0])[0] != 0);  //** Item has been picked up.
                }
                ItemsPickedUp += 1;
            } while (ItemsPickedUp != itemsToPickup);
        }

        public static int GetItemId(int bagId, int slot)
        {
            Cmd(Ca.SetBag, bagId);

            return CmdCb(Ca.GetItemId, slot)[0];
        }

        public static int[] GetItemInfo(int bagId, int slot)
        {
            Cmd(Ca.SetBag, bagId);

            return CmdCb(Ca.GetItemInfo, slot);
        }

        public static int[] GetItemLastModifier(int bagId, int slot)
        {
            Cmd(Ca.SetBag, bagId);

            return CmdCb(Ca.GetItemLastModifier, slot);
        }

        /* MoveToEx method: returns 0 if move is successful. 
           Returns -1 if dead, returns -2 changed, returns -3 if blocked.
         */

        public static int MoveToEx(float x, float y)
        {
            return MoveToEx(x, y, 50);
        }

        public static int MoveToEx(float x, float y, int random)
        {
            return MoveToEx(x, y, 50, random);
        }

        public static int MoveToEx(float x, float y, int random, int timeout)
        {
            int SavedMapState, CurrentMapState, Blocked = 0;
            float[] SavedCoords, CurrentCoords;
            GwcaTimer RetryTimer = new GwcaTimer();

            SavedMapState = CmdCb(Ca.GetMapLoading)[0];

            MoveEx(x, y, random);

            SavedCoords = GetCoords(-2);

            do
            {
                Gw.Sleep(250);

                if (IsDead(-2) == true)
                {
                    RetryTimer.Kill();
                    return -1;
                }

                CurrentMapState = CmdCb(Ca.GetMapLoading)[0];
                if (SavedMapState != CurrentMapState)
                {
                    RetryTimer.Kill();
                    return -2;
                }

                CurrentCoords = GetCoords(-2);
                Gw.Sleep(25, 50);
                if (SavedCoords[0] == CurrentCoords[0] && SavedCoords[1] == CurrentCoords[1])
                {
                    Blocked += 1;
                    MoveEx(x, y, random);
                }

                if (RetryTimer.Duration >= timeout)
                {
                    Gw.MoveEx(x, y, random);
                }

            } while (ComputeDistanceEx(CurrentCoords[0], CurrentCoords[1], x, y) > 250 && Blocked < 20);

            RetryTimer.Kill();

            if (Blocked >= 20)
            {
                MoveEx(x, y, random);
                return -3;
            }


            return 0;
        }

        public static int ComputeDistanceEx(float[] coords1, float[] coords2)
        {
            return ComputeDistanceEx(coords1[0], coords1[1], coords2[0], coords2[1]);
        }

        public static int ComputeDistanceEx(float x1, float y1, float x2, float y2)
        {
            return Convert.ToInt32(Math.Sqrt((Math.Pow((y2 - y1), 2) + (Math.Pow((x2 - x1), 2)))));
        }

        public static void UseSkillEx(int skillSlot)
        {
            UseSkillEx(skillSlot, 0);
        }

        public static void UseSkillEx(int skillSlot, int targetId)
        {
            GwcaTimer Deadlock = new GwcaTimer();  //** 'new GwcaTimer();' is exactly the same as TimerInit()

            Cmd(Ca.UseSkill, skillSlot, targetId);

            do
            {
                if (IsDead(-2) == true)
                {
                    break;
                }

                if (CmdCb(Ca.GetSkill, -2)[0] == 0 && Deadlock.Duration > 1000)
                {
                    break;
                }
            } while (CmdCb(Ca.SkillRecharge, skillSlot)[0] == 0 || Deadlock.Duration < 15000);

            Deadlock.Kill();
        }


        #endregion

        #region GWCA.NET Extra Functions
        /* <summary>
         * These are functions that do not neccessarily have an origin in 
         * the original GWCAConstants.au3 for AutoIt.  Some of these functions
         * are critical to GWCA C# operation, while others are added for easier scripting.
         * </summary> */

        //** Required to locate the Guild Wars instance, returns IntPtr
        public static IntPtr GetGwProcessHandle()
        {
            return MemoryAccess.GetProcessHandle("Gw");
        }

        //** Return's pid of GW instance
        public static int GetGwProcessId()
        {
            return MemoryAccess.GetProcessId("Gw");
        }

        //** Gets current character name
        public static string GetName(int agentId)
        {
            using (MemoryAccess GwProc = new MemoryAccess(GetGwProcessId()))
            {
                return GwProc.ReadMemory((IntPtr)CmdCb(Ca.GetName, agentId)[0], 0x20);
            }
        }

        //** Gets current x and y Coordinates
        public static float[] GetCoords(int agentId)
        {
            int[] CurrentCoords = new int[2];
            float[] ReturnCoords = new float[2];
            int i = 0;

            CurrentCoords = CmdCb(Ca.GetCoords, agentId);

            foreach (int x in CurrentCoords)
            {
                ReturnCoords[i] = IntToFloat(x);

                i++;
            }

            return ReturnCoords;
        }

        //** Resigns and automatically returns character to outpost
        public static void Resign()
        {
            Cmd(Ca.Resign, 0, 0);

            do
            {
                Gw.Sleep(750, 1250);

                if (CmdCb(Ca.GetDead)[0] > 0)  //** Player is dead
                {
                    Cmd(Ca.ReturnToOutpost);
                }
            } while (CmdCb(Ca.GetMapLoading)[0] != 0); //** Map has not loaded
        }

        //** Send chat message to Gw, all chat by default unless channel is specified (See overload).
        public static void SendChat(String chatMessage)
        {
            SendChat(chatMessage, Chat.All);
        }

        public static void SendChat(String chatMessage, Chat chatChannel)
        {
            int MemPtr = 0;

            //** Automatically set emote channel if '/' exists @ beginning of string.
            if (chatMessage.IndexOf("/", 0) == 0)
            {
                chatChannel = Chat.Emote;
                chatMessage = chatMessage.Split('/')[1];
            }

            MemPtr = CmdCb(Ca.AllocMem, (chatMessage.Length + 1) * 2)[1];

            using (MemoryAccess GwProc = new MemoryAccess(Gw.GetGwProcessId()))
            {
                GwProc.WriteMemory((IntPtr)MemPtr, chatMessage, 2);
            }

            Gw.Sleep(50);

            Cmd(Ca.SendChat, (int)chatChannel, MemPtr);

            Cmd(Ca.FreeMem, 0, MemPtr);
        }

        //** Sleep function
        public static void Sleep(int milliseconds)
        {
            Sleep(milliseconds, milliseconds);
        }

        public static void Sleep(int minMilliseconds, int maxMilliseconds)
        {
            System.Random Rand = new Random();

            Thread.Sleep(Rand.Next(minMilliseconds, maxMilliseconds));
        }

        //** Easy change target function.
        public static void SelectTarget(int targetId)
        {
            Cmd(Ca.ChangeTarget, targetId);
        }

        //** Several GetIs functions.
        public static Boolean IsAttacking(int agentId)
        {
            switch (CmdCb(Ca.GetIsAttacking, agentId)[0])
            {
                case 0:
                    return false;
                case 1:
                    return true;
                default:
                    return false;
            }
        }

        public static Boolean IsCasting(int agentId)
        {
            switch (CmdCb(Ca.GetIsCasting, agentId)[0])
            {
                case 0:
                    return false;
                case 1:
                    return true;
                default:
                    return false;
            }
        }

        public static Boolean IsDead(int agentId)
        {
            switch (CmdCb(Ca.GetIsDead, agentId)[0])
            {
                case 0:
                    return false;
                case 1:
                    return true;
                default:
                    return false;
            }
        }

        public static Boolean IsKnockedDown(int agentId)
        {
            switch (CmdCb(Ca.GetIsKnockedDown, agentId)[0])
            {
                case 0:
                    return false;
                case 1:
                    return true;
                default:
                    return false;
            }
        }

        public static Boolean IsMoving(int agentId)
        {
            switch (CmdCb(Ca.GetIsMoving, agentId)[0])
            {
                case 0:
                    return false;
                case 1:
                    return true;
                default:
                    return false;
            }
        }
        #endregion
    }

    #region Other Classes

    public class MemoryAccess : IDisposable
    {
        /* <summary>
         * I've written this class to easily make quick read and writes to the Gw process.
         * By default, GWCA should already do most of what we need.  However, in the event
         * that we still need to access the memory, I've added this class to make things easier on the end-user.
         * The reason I inherited from IDisposable was to ensure our handles were getting closed everytime we used
         * any of these methods.
         * 
         * All that being said, if you don't know what you're doing, don't mess with these functions.
         * </summary> */

        private IntPtr ProcessHandle;
        private uint ProcessId;
        private Byte[] Buffer;
        private bool Disposed;



        public MemoryAccess(int ProcessId)
        {
            this.ProcessId = (uint)ProcessId;
            this.ProcessHandle = Win32Api.OpenProcess(0x1F0FFF, false, ProcessId);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool Disposing)
        {
            if (!Disposed)
            {
                if (Disposing)
                {
                    if (ProcessHandle != IntPtr.Zero)
                    {
                        Win32Api.CloseHandle(ProcessHandle);
                        ProcessHandle = IntPtr.Zero;
                    }
                }

                Disposed = true;
            }
        }

        //** Required to locate the Guild Wars instance, returns IntPtr handle of target process.
        public static IntPtr GetProcessHandle(string ProcessName)
        {
            try
            {
                Process[] Processes = Process.GetProcessesByName(ProcessName);
                foreach (Process p in Processes)
                {
                    return p.MainWindowHandle;
                }
            }
            catch (Exception e)
            {
                //** TODO: Handle exceptions
            }

            return IntPtr.Zero;
        }

        //** Return's pid of of target process
        public static int GetProcessId(string ProcessName)
        {
            try
            {
                Process[] Processes = Process.GetProcessesByName(ProcessName);
                foreach (Process p in Processes)
                {
                    return p.Id;
                }
            }

            catch (Exception e)
            {
                //** TODO: Handle exceptions
            }

            return -1;
        }

        //** TODO: More overloads for ReadMemory to read other and different types

        public String ReadMemory(IntPtr Address, uint BytesToRead)
        {
            this.Buffer = new Byte[BytesToRead + 1];
            Win32Api.ReadProcessMemory(this.ProcessHandle, Address, this.Buffer, BytesToRead, 0);
            return System.Text.UnicodeEncoding.Unicode.GetString(this.Buffer);
        }

        public Boolean WriteMemory(IntPtr Address, String Data, int AdditionalBytesToWrite)
        {
            return WriteMemory(Address, Data, AdditionalBytesToWrite, System.Text.Encoding.Unicode);
        }

        public Boolean WriteMemory(IntPtr Address, String Data, int AdditionalBytesToWrite, System.Text.Encoding StringEncoding)
        {
            if (StringEncoding.EncodingName.Equals(System.Text.Encoding.ASCII.EncodingName))
            {
                return WriteMemory(Address, System.Text.ASCIIEncoding.ASCII.GetBytes(Data), System.Text.ASCIIEncoding.ASCII.GetBytes(Data).Length + AdditionalBytesToWrite);
            }
            else
            {
                return WriteMemory(Address, System.Text.UnicodeEncoding.Unicode.GetBytes(Data), System.Text.UnicodeEncoding.Unicode.GetBytes(Data).Length + AdditionalBytesToWrite);
            }
        }

        public Boolean WriteMemory(IntPtr Address, Byte[] Data)
        {
            return WriteMemory(Address, Data, Data.Length);
        }

        public Boolean WriteMemory(IntPtr Address, Byte[] Data, int SpecifiedSize)
        {
            this.Buffer = Data;
            return Win32Api.WriteProcessMemory(this.ProcessHandle, Address, this.Buffer, (uint)SpecifiedSize, 0);
        }

        //** TODO: Clean up and remove unneccessary overloads

        //** As of 1.0.204.0, Built in DLL injection.  If method has no parameters, it will attempt to load Graphics.dll from current directory.
        public Boolean InjectLibrary()
        {
            return InjectLibrary(System.IO.Directory.GetCurrentDirectory() + "\\Graphics.dll");
        }

        public Boolean InjectLibrary(string PathToLibrary)
        {
            if (System.IO.File.Exists(PathToLibrary) == false)
            {
                return false;
            }

            IntPtr KernelPointer = IntPtr.Zero;
            IntPtr RemoteThread = IntPtr.Zero;
            IntPtr RemoteMemory = IntPtr.Zero;

            if (this.ProcessHandle != IntPtr.Zero)
            {
                //** Pointer to hold Kernel32 handle for later use
                KernelPointer = Win32Api.GetModuleHandle("Kernel32");

                //** Allocate memory
                RemoteMemory = Win32Api.VirtualAllocEx(this.ProcessHandle, IntPtr.Zero, 0x1000, 0x1000, 0x004);

                //** Check if memory was allocated or not.
                if (RemoteMemory == IntPtr.Zero)
                {
                    return false;
                }

                WriteMemory(RemoteMemory, PathToLibrary, 2, System.Text.Encoding.ASCII);

                //** Create remote thread using the path stored in target process memory.
                RemoteThread = Win32Api.CreateRemoteThread(this.ProcessHandle, IntPtr.Zero, 0,
                          Win32Api.GetProcAddress(KernelPointer, "LoadLibraryA"), RemoteMemory, 0, IntPtr.Zero);

                Win32Api.CloseHandle(RemoteThread);

                return true;
            }

            return false;
        }
    }

    public class GwcaTimer : System.Windows.Forms.Timer
    {
        /* <summary>
         * This is a simple class derrived from the default windows timer that
         * has an added member, Duration, so that it can be used similarly to the AutoIt timers.
         * 
         * NEEDS WORK THOUGH
         * 
         * </summary> */

        private int _duration;

        public int Duration
        {
            get { return _duration; }
            set { _duration = value; }
        }


        public GwcaTimer()
            : base()
        {
            this.Tick += new EventHandler(OnTick);
            this.Interval = 100;
            this.Enabled = true;
        }

        public void OnTick(object sender, System.EventArgs e)
        {
            _duration += 100;
        }

        public void Kill()
        {
            this.Stop();
            this.Dispose();
        }
    }

    public class GwcaPipe
    {
        private static NamedPipeClientStream _gwcaPipe = null;

        public enum MessageType : ushort
        {
            Is_Numeric = 0x00,
            Is_Text,
            Is_Command,
            Is_Request = 0x04,
            Is_Response = 0x08
        };

        static private bool IsPipeConnectedOrCreated(String pipeName)
        {
            try
            {
                if (_gwcaPipe == null || _gwcaPipe.IsConnected == false)
                {
                    _gwcaPipe = new NamedPipeClientStream(".", pipeName);
                    _gwcaPipe.Connect(1000);
                }

                if (_gwcaPipe.IsConnected && (_gwcaPipe.CanRead || _gwcaPipe.CanWrite))
                {
                    return true;
                }
            }
            catch (Exception Exception)
            {
                Debug.Print(Exception.Message);
            }

            return false;
        }

        static public int[] SendMessage(MessageType messageType, Ca msg, object wParam, object lParam)
        {
            return SendMessage(messageType, msg, wParam, lParam, String.Format("GWCA_{0}", Gw.GetGwProcessId()));
        }

        static public int[] SendMessage(MessageType messageType, Ca msg, object wParam, object lParam, string pipeName)
        {
            if (IsPipeConnectedOrCreated(pipeName) == true)
            {
                IntPtr PipeMessagePtr = default(IntPtr);
                BaseMessage PipeMessage = new BaseMessage(msg, messageType, wParam, lParam);
                int StructSize;

                try
                {
                    StructSize = Marshal.SizeOf(PipeMessage);

                    PipeMessagePtr = Marshal.AllocHGlobal(StructSize);

                    Marshal.StructureToPtr(PipeMessage, PipeMessagePtr, false);
                    byte[] StreamData = new byte[StructSize];

                    if (messageType == MessageType.Is_Command)
                    {
                        Marshal.Copy(PipeMessagePtr, StreamData, 0, StructSize);
                        _gwcaPipe.Write(StreamData, 0, StructSize);
                    }
                    else if (messageType == MessageType.Is_Request)
                    {
                        Marshal.Copy(PipeMessagePtr, StreamData, 0, StructSize);
                        _gwcaPipe.Write(StreamData, 0, StructSize);

                        _gwcaPipe.Read(StreamData, 0, StructSize);
                        Marshal.Copy(StreamData, 0, PipeMessagePtr, StructSize);
                        Marshal.PtrToStructure(PipeMessagePtr, PipeMessage);
                    }
                }
                catch (Exception Exception)
                {
                    Debug.Print(Exception.Message);
                    return Gw.CbVar;
                }
                finally
                {
                    if (messageType == MessageType.Is_Request)
                    {
                        Gw.CbVar[0] = PipeMessage.WParam.i_Param;
                        Gw.CbVar[1] = PipeMessage.LParam.i_Param;

                        Marshal.FreeHGlobal(PipeMessagePtr);
                    }
                }
            }

            return Gw.CbVar;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class BaseMessage
        {
            public ushort Header;
            public ushort Type;
            public Param_t WParam;
            public Param_t LParam;

            public BaseMessage(Ca Header, MessageType Type, object wParam, object lParam)
            {
                this.Header = (ushort)Header;
                this.Type = (ushort)Type;
                this.WParam = new Param_t(wParam);
                this.LParam = new Param_t(lParam);
            }

            [StructLayout(LayoutKind.Explicit)]
            public struct Param_t
            {
                [FieldOffset(0)]
                public Int32 i_Param;
                [FieldOffset(0)]
                public UInt32 d_Param;
                [FieldOffset(0)]
                public float f_Param;

                public Param_t(object Param)
                {
                    i_Param = 0;
                    d_Param = 0;
                    f_Param = 0;

                    if (Param.GetType() == typeof(Int32))
                    {
                        i_Param = Convert.ToInt32(Param);
                    }
                    else if (Param.GetType() == typeof(UInt32))
                    {
                        d_Param = Convert.ToUInt32(Param);
                    }
                    else if (Param.GetType() == typeof(Single))
                    {
                        f_Param = Convert.ToSingle(Param);
                    }
                }
            }

            public struct cbStruct
            {
                public Param_t WParam { get; set; }
                public Param_t LParam { get; set; }
            }
        }
    }
    #endregion
}

 